from datetime import datetime
from rest_framework import serializers
from django.db import transaction
from rest_framework import status
from . import models
from django_redis import get_redis_connection
from course.models import (Course, CourseExpire)
from coupon.models import (Coupon, UserCoupon)
from luffy.settings.contants import CREDIT_MONEY


class OrderModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Order
        fields = ['id', 'order_number', 'pay_type', 'credit', 'coupon']
        extra_kwargs = {
            'id': {'read_only': True},
            'order_number': {'read_only': True},
            'pay_type': {'write_only': True},
            'credit': {'write_only': True},
            'coupon': {'write_only': True},
        }

    # 对用户提交过来的数据进行校验
    def validate(self, attrs):
        """
        全局校验
        :param attrs: 其中包含用户提交过来的数据
        :return:
        """
        # 获取用户ID
        request = self.context['request']
        user_id = request.user.id

        # 1、校验支付方式
        pay_type = attrs.get('pay_type')
        try:
            models.Order.pay_choices[pay_type]
        except:
            raise serializers.ValidationError('抱歉，您选择的支付方式不存在！')

        # 2、防止前端利用程序发送数据，故要设置其积分的上限以及优惠券的使用
        # 校验积分上限
        max_credit = request.user.credit
        use_credit = attrs.get('credit', 0)
        if use_credit != 0 and use_credit > max_credit:
            raise serializers.ValidationError('抱歉，使用积分超过上限！')

        # 校验优惠券是否存在，是否满足使用条件，是否过期
        use_coupon_id = attrs.get('coupon')
        if use_coupon_id > 0:
            try:
                user_coupon = UserCoupon.objects.get(is_show=True,
                                                     is_deleted=False,
                                                     pk=use_coupon_id,
                                                     user_id=user_id,
                                                     is_use=False, )
            except:
                raise serializers.ValidationError('当前优惠券不存在!')

            start_time = user_coupon.start_time.timestamp()
            end_time = user_coupon.end_time.timestamp()
            now_time = datetime.now().timestamp()
            if now_time < start_time:
                raise serializers.ValidationError('当前优惠劵尚未到使用时间!')
            if now_time > end_time:
                raise serializers.ValidationError('当前优惠劵已过期!')
        # todo 校验课程是否存在

        return attrs

    # 保存用户提交过来的数据，仅用 'ModelSerializer'提供的 'save' 方法再调用 'create'方法
    # 无法返回给用户需要的相应数据，比如：订单号等等

    # 故自己定义 'create' 方法，调用save方法时就会自动调用自己定义的'create' 方法
    def create(self, validated_data):
        """
        自定义保存数据方法
        :param validated_data: 校验通过的数据
        :return:
        """
        # 获取用户ID
        request = self.context['request']
        user_id = request.user.id

        # 获取用户选择的支付方式
        pay_type = validated_data.get('pay_type')

        # 获取用户选择的优惠券ID
        user_coupon_id = validated_data.get('coupon', 0)

        # 获取用户选择使用的积分数量
        use_credit = validated_data.get('credit', 0)

        # 1、生成订单号（每次自增1）
        # （时间日期 + 用户id + 自增值）
        # 获取当前时间
        current_time = datetime.now()

        # 借助 redis 提供的 incr 方法
        redis_conn = get_redis_connection('cart')
        incr = redis_conn.incr('order')  # 自增数据是 int 类型

        order_number = current_time.strftime('%Y%m%d%H%M%S') + '%06d' % user_id + '%06d' % incr

        pipe = redis_conn.pipeline()
        pipe.multi()

        with transaction.atomic():
            sid = transaction.savepoint()
            order = models.Order.objects.create(**{
                'order_title': '购买路飞学城xx课程',
                'total_price': 0,
                'real_price': 0,
                'order_number': order_number,
                'order_status': 0,
                'pay_type': pay_type,
                'coupon': user_coupon_id,
                'credit': use_credit,
                'order_desc': '',
                'pay_time': current_time,
                'user_id': user_id,
            })

            # 生成订单详情
            cart_data_dict = redis_conn.hgetall("cart_%s" % user_id)
            selected_course_data = redis_conn.smembers("selected_%s" % user_id)

            total_p = 0
            real_p = 0
            for course_id_bytes, expire_bytes in cart_data_dict.items():

                course_id = int(course_id_bytes.decode())
                expire_id = int(expire_bytes.decode())
                if course_id_bytes in selected_course_data:
                    try:
                        course_obj = Course.objects.get(pk=course_id)
                    except:
                        continue

                    try:
                        if expire_id > 0:
                            expire_obj = CourseExpire.objects.get(is_show=True, is_deleted=False, id=expire_id)

                    except CourseExpire.DoesNotExist:

                        transaction.savepoint_rollback(sid)
                        raise serializers.ValidationError('有效期不存在')

                    origin_price = course_obj.price
                    real_price = course_obj.real_price(expire_id)
                    discount_name = course_obj.discount_name

                    # 保存订单详情数据
                    # 方法一：
                    # models.OrderDetail.objects.create(
                    #     order=order,
                    #     course=course_obj,
                    #     expire=expire_id,
                    #     price=origin_price,
                    #     real_price= real_price,
                    #     discount_name= discount_name,
                    # )

                    # 方法二：
                    models.OrderDetail.objects.create(**{
                        'order': order,
                        'course': course_obj,
                        'expire': expire_id,
                        'price': origin_price,
                        'real_price': real_price,
                        'discount_name': discount_name,
                    })

                    total_p += float(origin_price)
                    real_p += float(real_price)

                    # 当生成订单之后，就把购物车里面的选中的数据给删除掉，没有什么用了
                    pipe.hdel('cart_%s' % user_id, course_id)
                    pipe.srem('selected_%s' % user_id, course_id)

            # 使用优惠劵之后的原价以及真实价格计算
            if user_coupon_id > 0:
                try:
                    user_coupon_obj = UserCoupon.objects.get(pk=user_coupon_id)
                    if user_coupon_obj.coupon.condition > real_p:
                        transaction.savepoint_rollback(sid)
                        raise serializers.ValidationError('抱歉，购买课程实际支付价格小于优惠劵所优惠的价格，故订单生成失败！')
                    sale = user_coupon_obj.coupon.sale
                    sale_num = float(sale[1:])
                    if sale[0] == "*":
                        real_p = real_p * sale_num
                    else:
                        real_p = real_p - sale_num
                except:
                    transaction.savepoint_rollback(sid)
                    raise serializers.ValidationError('订单生成失败!')

            # 使用积分之后的原价以及真实价格计算
            if use_credit > 0:
                if use_credit > real_p * CREDIT_MONEY:
                    transaction.savepoint_rollback(sid)
                    raise serializers.ValidationError('抱歉，订单生成失败！提示：积分使用数量不可超过本次实际支付的金额！')

                real_p = float('%.2f' % (real_p - use_credit / CREDIT_MONEY))

            # 经过优惠劵以及积分抵扣后的原价以及真实价格
            order.total_price = total_p
            order.real_price = real_p

        try:
            pipe.execute()
            order.save()
        except:
            transaction.savepoint_rollback(sid)
            raise serializers.ValidationError('订单生成失败！')

        return order


class UserOrderModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Order
        fields = ['id', 'order_number', 'order_status', 'pay_time', 'course_list']
